// Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the GNU General Public License version 2 or later.
package fit.eg.bowling;

public class BowlingGame implements Bowling {
	private static final int FIRST_BALL_IN_FRAME = 0;
	private static final int SECOND_BALL_IN_FRAME = 1;
	private static final int GAME_OVER = 2;
	private static final int BALL_AFTER_SPARE = 3;
	private static final int BALL_AFTER_TENTH_FRAME_SPARE = 4;
	private static final int BALL_AFTER_FIRST_STRIKE = 5;
	private static final int BALL_AFTER_SECOND_STRIKE = 6;

	private int currentFrame = 1;
	private int currentBall = 1;
	private int scoreableFrame = 0;
	private boolean gameOver = false;

	private int state = FIRST_BALL_IN_FRAME;
	private final BowlingScorer bowlingScorer = new BowlingScorer();

	private void changeState() {
		switch (state) {
		case FIRST_BALL_IN_FRAME:
			firstBallInFrame();
			break;

		case SECOND_BALL_IN_FRAME:
			secondBallInFrame();
			break;

		case BALL_AFTER_SPARE:
			ballAfterSpare();
			break;

		case BALL_AFTER_TENTH_FRAME_SPARE:
			endGame();
			break;

		case BALL_AFTER_FIRST_STRIKE:
			ballAfterFirstStrike();
			break;

		case BALL_AFTER_SECOND_STRIKE:
			ballAfterSecondStrike();
			break;
		}
	}

	private void firstBallInFrame() {
		if (bowlingScorer.lastRollWasStrike()) {
			state = BALL_AFTER_FIRST_STRIKE;
			incrementFrame();
		} else {
			state = SECOND_BALL_IN_FRAME;
			currentBall = 2;
		}
	}

	private void secondBallInFrame() {
		if (bowlingScorer.lastRollWasSpare() && currentFrame == 10) {
			state = BALL_AFTER_TENTH_FRAME_SPARE;
			currentBall = 3;
		} else if (bowlingScorer.lastRollWasSpare()) {
			state = BALL_AFTER_SPARE;
			incrementFrame();
		} else if (currentFrame == 10) {
			endGame();
		} else {
			state = FIRST_BALL_IN_FRAME;
			scoreableFrame = currentFrame;
			incrementFrame();
		}
	}

	private void ballAfterSpare() {
		if (bowlingScorer.lastRollWasStrike()) {
			state = BALL_AFTER_FIRST_STRIKE;
			scoreableFrame++;
			incrementFrame();
		} else {
			state = SECOND_BALL_IN_FRAME;
			currentBall = 2;
			scoreableFrame++;
		}
	}

	private void ballAfterFirstStrike() {
		if (bowlingScorer.lastRollWasStrike()) {
			state = BALL_AFTER_SECOND_STRIKE;
			incrementFrame();
		} else {
			state = SECOND_BALL_IN_FRAME;
			currentBall = 2;
		}
	}

	private void ballAfterSecondStrike() {
		if (bowlingScorer.lastRollWasStrike() && currentFrame == 10
				&& currentBall == 3) {
			endGame();
		} else if (bowlingScorer.lastRollWasStrike()) {
			incrementFrame();
			scoreableFrame++;
		} else {
			state = SECOND_BALL_IN_FRAME;
			currentBall = 2;
			scoreableFrame++;
		}
	}

	private void endGame() {
		state = GAME_OVER;
		currentBall = 0;
		scoreableFrame = 10;
		gameOver = true;
	}

	private void incrementFrame() {
		if (currentFrame < 10) {
			currentFrame++;
			currentBall = 1;
		} else
			currentBall++;
	}

	@Override
	public int currentFrame() {
		return currentFrame;
	}

	@Override
	public int currentBall() {
		return currentBall;
	}

	@Override
	public int scoreableFrame() {
		return scoreableFrame;
	}

	@Override
	public boolean validGame() {
		return true;
	}

	@Override
	public boolean gameOver() {
		return currentFrame == 10 && currentBall == 0;
	}

	@Override
	public boolean isGameOver() {
		return gameOver;
	}

	@Override
	public void roll(int pins) {
		bowlingScorer.roll(pins);
		changeState();
	}

	@Override
	public int score(int frame) {
		return bowlingScorer.score(frame);
	}
}
